package org.dromara.authcode.service.impl;

import org.dromara.authcode.domain.StatusCode;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.dromara.authcode.domain.bo.AuthCodeBo;
import org.dromara.authcode.domain.vo.AuthCodeVo;
import org.dromara.authcode.domain.AuthCode;
import org.dromara.authcode.mapper.AuthCodeMapper;
import org.dromara.authcode.service.IAuthCodeService;

import java.security.SecureRandom;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 激活码Service业务层处理
 *
 * @author Miles Meng
 * @date 2025-08-08
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class AuthCodeServiceImpl implements IAuthCodeService {
    private final AuthCodeMapper baseMapper;
    // 从配置中读取有效期（单位：天）
    @Value("${app.authCodeExpireMonths:12}")
    private Long expiryMonths;

    // 从配置中读取免费天数（单位：天）
    @Value("${app.authCodeUseDays:7}")
    private int useDays;

    private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
    private static final String UPPER = LOWER.toUpperCase();
    private static final String DIGITS = "0123456789";
    private static final String ALPHANUM = UPPER + DIGITS;
    private static final SecureRandom random = new SecureRandom();
    /**
     * 查询激活码
     *
     * @param id 主键
     * @return 激活码
     */
    @Override
    public AuthCodeVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 根据激活码查询激活码
     *
     * @param code 激活码
     * @return 激活码
     */
    @Override
    public AuthCode queryByCode(String code){
        return baseMapper.selectOne(new LambdaQueryWrapper<AuthCode>().eq(AuthCode::getCode, code));
    }
    /**
     * 分页查询激活码列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 激活码分页列表
     */
    @Override
    public TableDataInfo<AuthCodeVo> queryPageList(AuthCodeBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<AuthCode> lqw = buildQueryWrapper(bo);
        Page<AuthCodeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的激活码列表
     *
     * @param bo 查询条件
     * @return 激活码列表
     */
    @Override
    public List<AuthCodeVo> queryList(AuthCodeBo bo) {
        LambdaQueryWrapper<AuthCode> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<AuthCode> buildQueryWrapper(AuthCodeBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<AuthCode> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(AuthCode::getId);
        lqw.eq(StringUtils.isNotBlank(bo.getCode()), AuthCode::getCode, bo.getCode());
        lqw.eq(bo.getUserId() != null, AuthCode::getUserId, bo.getUserId());
        lqw.eq(bo.getStatus() != null, AuthCode::getStatus, bo.getStatus());
        lqw.eq(bo.getExpireTime() != null, AuthCode::getExpireTime, bo.getExpireTime());
        lqw.eq(bo.getAuthTime() != null, AuthCode::getAuthTime, bo.getAuthTime());
        lqw.eq(bo.getGoodsId() != null, AuthCode::getGoodsId, bo.getGoodsId());
        lqw.eq(bo.getValidMonths() != null, AuthCode::getValidMonths, bo.getValidMonths());
        return lqw;
    }

    /**
     * 新增激活码
     *
     * @param bo 激活码
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(AuthCodeBo bo) {
        AuthCode add = MapstructUtils.convert(bo, AuthCode.class);
        add.setStatus(0L);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改激活码
     *
     * @param bo 激活码
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(AuthCodeBo bo) {
        AuthCode update = MapstructUtils.convert(bo, AuthCode.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(AuthCode entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除激活码信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    /**
     * 生成新的激活码
     *
     * @return 新的激活码
     */
    public static String generate(int length, boolean letters, boolean numbers) {
        if (length <= 0) {
            throw new IllegalArgumentException("Length must be positive");
        }

        String pool = "";
        if (letters && numbers) {
            pool = ALPHANUM;
        } else if (letters) {
            pool = UPPER; // 只使用大写字母
        } else if (numbers) {
            pool = DIGITS;
        } else {
            throw new IllegalArgumentException("Must include letters or numbers");
        }
        // 使用Java 8 Stream生成随机字符串
        List<Character> chars = random.ints(length, 0, pool.length())
            .mapToObj(pool::charAt)
            .collect(Collectors.toList());

        // 打乱顺序增加随机性
        Collections.shuffle(chars);

        return chars.stream()
            .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
            .toString();
    }

    /**
     * 生成并插入新的激活码
     *
     * 该方法生成一个16位的随机激活码，包含字母和数字，默认状态下激活码是未使用的，并设置有效期为1年
     *
     * @return Boolean 生成的激活码是否成功
     */
    @Override
    public Boolean generateAuthCode() {
        String code = generate(16, true, true);
        AuthCode activationCode = new AuthCode();
        activationCode.setCode(code);
        activationCode.setStatus(StatusCode.F.getStatusCode());
        // 有效月数
        activationCode.setValidMonths(expiryMonths);

        return baseMapper.insert(activationCode) > 0;
    }
}
